// CalculateDate.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <Windows.h>
typedef struct _Date
{       
    int nYear;
    int nMonth;
    int nDay;
    _Date(int nM, int nD, int nY)
    {
        nYear = nY;
        nMonth = nM;
        nDay = nD;
    }
} Date, *PDate;

/*************************************************************************
/*Parameters:
/*@pCur                      [in] Points to the Date struct that specifies the origin time; 
/*@nDays                   [in] Days passed
/*@pRet                      [out] Points to the Date struct that receives the calculates result 
/*Return value:
/*If the function succeeds, 0 will be return
/*If the function fails, the return value is non-zero. 
**************************************************************************/
int CalculateDate(const PDate pCur, int nDays, PDate pRet)
{
    if (NULL == pCur || NULL == pRet )
    {
        _tprintf_s(_T("Invalid parameter!  NULL Pointer \n"));
        return ERROR_INVALID_PARAMETER;
    }
    if (pCur->nDay > 31 || pCur->nMonth > 12)
    {
        _tprintf_s(_T("Invalid parameter! The day can't bigger than 31, the month can't bigger than 12 \n"));
        return ERROR_INVALID_PARAMETER;
    }
    if (pCur->nMonth <=0 || pCur->nDay <= 0 ||  pCur->nYear <= 0)
    {
         _tprintf_s(_T("Invalid parameter! Time can't be negative or zero \n"));
        return ERROR_INVALID_PARAMETER;
    }

    memcpy_s(pRet, sizeof(Date), pCur, sizeof(Date));
    int nRestDays = nDays;
    while (nRestDays)
    {
        //Get the number of days in specified month

        int nDaysInMonth = 31;  //Default value is 31;
        if (pRet->nMonth == 4 || pRet->nMonth == 6 || pRet->nMonth == 9 || pRet->nMonth == 11 || pRet->nMonth == 2)
        {

            nDaysInMonth = 30;
            if (pRet->nMonth == 2)
            {
                //Take care of leap-year
                if((pRet->nYear%4==0&&pRet->nYear%100!=0)||(pRet->nYear%400==0)) 
                {
                    nDaysInMonth = 29;
                }
                else
                {
                    //There are 28 days  in Feb. if the year is not leap-year 
                    nDaysInMonth = 28;
                }
            }

            //Calculate
            if (pRet->nDay + nRestDays > nDaysInMonth)
            {
                //The days left should minus the number of days have passed from now to the first day of next month
                nRestDays = nRestDays - (nDaysInMonth - pRet->nDay) - 1; 
                pRet->nMonth ++;
                //We move into next month, so set the day as the first day of next month
                pRet->nDay = 1;    
              
                continue;
            }
            //Consider nDays may be negative number;
            else if (pRet->nDay + nRestDays < 0)
            {
                //Because nDay parameter is negative, we should add the number of days go back  from now to the last day of last month
                nRestDays = nRestDays + pRet->nDay ; 
                pRet->nMonth--;
                //We move into previous month, so set the day as the last day of last month
                // the last of month of April, Jun, Sep, Nov and Feb are March, May, Aug, Oct and Jan. They always have 31 days
                pRet->nDay = nDaysInMonth;                       
              
                continue;
            }
            else
            {
                pRet->nDay += nRestDays;
                nRestDays = 0;
                break;
            }
        }


        if (pRet->nMonth == 3 || pRet->nMonth == 5 || pRet->nMonth == 7 || pRet->nMonth == 10 ||  pRet->nMonth == 8)
        {
            int nDaysInMonth = 31;
            if (pRet->nDay + nRestDays > nDaysInMonth)
            {
                nRestDays = nRestDays - (nDaysInMonth - pRet->nDay) - 1; 
                pRet->nMonth ++;
                pRet->nDay = 1;    
                
                continue;
            }
            //Consider nDays may be negative number;
            else if (pRet->nDay + nRestDays < 0)
            {
                if (pRet->nMonth == 3)
                {
                    pRet->nMonth--;
                    nRestDays = nRestDays + pRet->nDay; 
                    //We move into Feb
                    //In Feb, it may have 28 or 29 days
                    if((pRet->nYear%4==0&&pRet->nYear%100!=0)||(pRet->nYear%400==0)) 
                    {
                        pRet->nDay = 29;
                    }
                    else
                    {
                        //There are 28 days  in Feb. if the year is not leap-year 
                        pRet->nDay = 28;
                    }
                    
                    continue;
                } 
                else
                {
                    nRestDays = nRestDays + pRet->nDay; 
                    pRet->nMonth--;
                    //We move into previous month, so set the day as the last day of last month
                    // the last of month of May, Jul, Oct are April, Jun and Seb. They always have 30 days
                    pRet->nDay = 30;                       
                  
                    continue;
                }
            } 
            else
            {
                pRet->nDay += nRestDays;
                nRestDays = 0;
                break;
            }
              

        }   // End of  if (pRet->nMonth == 3 || pRet->nMonth == 5 || pRet->nMonth == 7 || pRet->nMonth == 10)
   

        //Case for January
         if (pRet->nMonth == 1)
         {
             if (pRet->nDay + nRestDays > nDaysInMonth)
             {
                  nRestDays = nRestDays - (nDaysInMonth - pRet->nDay) - 1; 
                 pRet->nMonth ++;
                 pRet->nDay = 1;    
                 continue;
             }
             //Consider nDays may be negative number;
             else if (pRet->nDay + nDays < 0)
             {
                 //We move into previous year, so set the  date should be 12/31/xxxx
                 nRestDays = nRestDays +pRet->nDay;
                 pRet->nMonth = 12;
                 pRet->nDay = 31;                       
                 if (pRet->nYear > 1)
                 {
                     pRet->nYear --;
                 }
                 else
                 {
                     pRet->nYear =0; pRet->nMonth=1; pRet->nDay =1;
                     return -1;
                 }
                
                 continue;
             }
             else
             {
                 pRet->nDay += nRestDays;
                 nRestDays = 0;
                 break;
            }
         }

        //Case for December
         if (pRet->nMonth == 12)
         {
             if (pRet->nDay + nRestDays > nDaysInMonth)
             {
                 //We move into previous year, so set the  date should be 01/01/xxxx
                 nRestDays = nRestDays - (nDaysInMonth - pRet->nDay) - 1; 
                 pRet->nMonth = 1;
                 pRet->nDay = 1;
                 pRet->nYear ++;
                 
                 continue;
             }
             //Consider nDays may be negative number;
             else if (pRet->nDay + nDays < 0)
             {
                 //We move into previous year, so set the  date should be 12/31/xxxx
                 nRestDays = nRestDays + pRet->nDay;
                 pRet->nMonth--;
                 pRet->nDay = 30;                                        
                 continue;
             }
             else
             {
                 pRet->nDay += nRestDays;
                 nRestDays = 0;
                 break;
             }
         }

    }

    return 0;
}
    
int _tmain(int argc, _TCHAR* argv[])
{
 
    //Test cases :  
    // 06/05/2011, nDays = 31, the result should be 07/06/2011, if function give out 07/05/2011, It indicates coder owner doesn't consider month have 30 days 
    Date TestResult1 = Date(0, 0, 0);
    Date TestDate1 = Date(6, 5, 2011);
    int nDays = 31;
    int nRet = CalculateDate(&TestDate1, nDays, &TestResult1);
    _tprintf_s(L"The input date is %d/%d/%d \n", TestDate1.nMonth, TestDate1.nDay, TestDate1.nYear);
    _tprintf_s(L"The input days are %d \n", nDays);
    _tprintf_s(L"The result date is %d/%d/%d \n", TestResult1.nMonth, TestResult1.nDay, TestResult1.nYear);
    
    // 10/05/2011, nDays = -10; the result should be 09/25/2011, if function give out 09/26/2011, It indicates coder owner doesn't consider month have 30 days 
    Date TestResult2 = Date(0, 0, 0);
    Date TestDate2 = Date(10, 5, 2011);
    nDays = -10;
    nRet = CalculateDate(&TestDate2, nDays, &TestResult2);
    _tprintf_s(L"The input date is %d/%d/%d \n", TestDate2.nMonth, TestDate2.nDay, TestDate2.nYear);
    _tprintf_s(L"The input days are %d \n", nDays);  
    _tprintf_s(L"The result date is %d/%d/%d \n", TestResult2.nMonth, TestResult2.nDay, TestResult2.nYear);

    // 01/05/2011, nDays = -40; the result should be 11/26/2010, it function doesn't give out year as 2010, It indicates code owner doesn't consider  in Jan, it may be go back to last year.
   // In this test case, if the result is 11/27/2010, It indicates coder owner doesn't consider month have 30 days 
    Date TestResult3 = Date(0, 0, 0);
    Date TestDate3 = Date(1, 5, 2011);
    nDays = -40;
    nRet = CalculateDate(&TestDate3,  nDays, &TestResult3);
    _tprintf_s(L"The input date is %d/%d/%d \n", TestDate3.nMonth, TestDate3.nDay, TestDate3.nYear);
    _tprintf_s(L"The input days are %d\n", nDays);  
    _tprintf_s(L"The result date is %d/%d/%d \n", TestResult3.nMonth, TestResult3.nDay, TestResult3.nYear);   
    
    // 12/25/2011, nDays = 10; the result should be 01/04/2012, it function doesn't give out year as 2012, It indicates code owner doesn't consider  in Dec, it may be move to next year.
    Date TestResult4 = Date(0, 0, 0);
    Date TestDate4 = Date(12, 25, 2011);
    nDays = 10;
    nRet = CalculateDate(&TestDate4,  nDays, &TestResult4);
    _tprintf_s(L"The input date is %d/%d/%d \n", TestDate4.nMonth, TestDate4.nDay, TestDate4.nYear);
    _tprintf_s(L"The input days are %d\n", nDays);  
    _tprintf_s(L"The result date is %d/%d/%d \n", TestResult4.nMonth, TestResult4.nDay, TestResult4.nYear);   


    // 02/20/2011, nDay = 10, the result should be 03/02/2011. if the result is 02/30/2011, God ! May be you want to ask the coder owner if he/she is kidding us.
    Date TestResult5 = Date(0, 0, 0);
    Date TestDate5 = Date(2, 20, 2011);
    nDays = 10;
    nRet = CalculateDate(&TestDate5,  nDays, &TestResult5);
    _tprintf_s(L"The input date is %d/%d/%d \n", TestDate5.nMonth, TestDate5.nDay, TestDate5.nYear);
    _tprintf_s(L"The input days are %d\n", nDays);  
    _tprintf_s(L"The result date is %d/%d/%d \n", TestResult5.nMonth, TestResult5.nDay, TestResult5.nYear);   
    
    //02/20/2000, nDay = 9,   leap-year, good result is 02/29/2000
    Date TestResult6 = Date(0, 0, 0);
    Date TestDate6 = Date(2, 20, 2000);
    nDays = 9;
    nRet = CalculateDate(&TestDate6,  nDays, &TestResult6);
    _tprintf_s(L"The input date is %d/%d/%d \n", TestDate6.nMonth, TestDate6.nDay, TestDate6.nYear);
    _tprintf_s(L"The input days are %d\n", nDays);  
    _tprintf_s(L"The result date is %d/%d/%d \n", TestResult6.nMonth, TestResult6.nDay, TestResult6.nYear);   


    //01/01/800, -65535 if the year in result is negative, it indicates coder owner doesn't consider the begin of the world can't not earlier than 01/01/0000 :)

    Date TestResult7 = Date(0, 0, 0);
    Date TestDate7 = Date(1, 1, 800);
    nDays = -65535;
    nRet = CalculateDate(&TestDate7,  nDays, &TestResult7);
    _tprintf_s(L"The input date is %d/%d/%d \n", TestDate7.nMonth, TestDate7.nDay, TestDate7.nYear);
    _tprintf_s(L"The input days are %d\n", nDays);  
    _tprintf_s(L"The result date is %d/%d/%d \n", TestResult7.nMonth, TestResult7.nDay, TestResult7.nYear);   

    //15/02/2011 some input date like this should be identified as invalid and the function should return immediately. 
    Date TestResult8 = Date(0, 0, 0);
    Date TestDate8 = Date(15, 2, 2011);
    nDays = 9;
    nRet = CalculateDate(&TestDate8,  nDays, &TestResult8);
    _tprintf_s(L"The input date is %d/%d/%d \n", TestDate8.nMonth, TestDate8.nDay, TestDate8.nYear);
    _tprintf_s(L"The input days are %d\n", nDays);  
    _tprintf_s(L"The result date is %d/%d/%d \n", TestResult8.nMonth, TestResult8.nDay, TestResult8.nYear);   

    Date TestResult9 = Date(0, 0, 0);
    Date TestDate9 = Date(1, 1, 2011);
    nDays = 365;
    nRet = CalculateDate(&TestDate9,  nDays, &TestResult9);
    _tprintf_s(L"The input date is %d/%d/%d \n", TestDate9.nMonth, TestDate9.nDay, TestDate9.nYear);
    _tprintf_s(L"The input days are %d\n", nDays);  
    _tprintf_s(L"The result date is %d/%d/%d \n", TestResult9.nMonth, TestResult9.nDay, TestResult9.nYear);   

    _tprintf_s(L"Press any key to continue...\n");
    getchar();
	return 0;
}

